package org.redmagic.client;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.redmagic.spring.RedMagicStringValueResolver;

import com.google.common.io.Files;

/**
 * This class is used to configure the magic-config file before
 * it is used.  It will replace placeholders that exist in the 
 * magic-config file with either a well-known property or one
 * that is looked up from a properties file.  The well-known
 * properties will be overridden by custom properties if both
 * exist with the same key.
 */
public class RedMagicConfigInitializer {
	
	private static final Log LOG = LogFactory.getLog(RedMagicConfigInitializer.class);
	
	private static final RedMagicStringValueResolver resolver = new RedMagicStringValueResolver();
	
	private static final String HOSTNAME = "HOSTNAME";
	private static final String FQDN = "FQDN";
	private static final String IP_ADDRESS = "IP_ADDRESS";
	
	private static Properties wellKnownProperties = new Properties();
	
	// Private so an instance can't be made
	private RedMagicConfigInitializer() {}
	
	// Build the wellKnownProperties map
	static {
		try {
			InetAddress localhost = InetAddress.getLocalHost();
			
			wellKnownProperties.put(HOSTNAME, localhost.getHostName());
			wellKnownProperties.put(FQDN, localhost.getCanonicalHostName());
			wellKnownProperties.put(IP_ADDRESS, localhost.getHostAddress());

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public static String prepareMagicConfig(String magicConfigFileName, List<String> magicConfigSetupFileNames) {
		
		Properties resolvedProperties = new Properties(wellKnownProperties);
		
		// Load properties from the magic config setup files
		if (magicConfigSetupFileNames != null) {
			for (String magicConfigSetupFileName : magicConfigSetupFileNames) {
				String resolvedFileName = resolvePlaceholders(magicConfigSetupFileName, resolvedProperties);
				resolvedProperties.putAll(loadPlaceholders(resolvedFileName));
			}
		}
		if (LOG.isDebugEnabled()) {
			LOG.debug("Placeholders for magic config file: " + resolvedProperties);
		}

		// Resolve any placeholders in the file name
		magicConfigFileName = resolvePlaceholders(magicConfigFileName, resolvedProperties);
        
        try {
        	// Load the original magic config file
	        String magicConfig = Files.toString(new File(magicConfigFileName), Charset.defaultCharset());
	        
	        // Resolve any placeholders in the magic config file
	        magicConfig = resolvePlaceholders(magicConfig, resolvedProperties);
	        
	        // Check for any remaining placeholders in the magic config file
	        Set<String> unresolvedPlaceholders = resolver.resolveStringValueAsync(magicConfig);
	        if (!unresolvedPlaceholders.isEmpty()) {
	        	LOG.error("Unable to resolve the following placeholders: " + unresolvedPlaceholders);
	        	throw new RuntimeException("Unable to resolve the following placeholders: " + unresolvedPlaceholders);
	        }
	        
	        // Return the fully resolved magic config file
	        return magicConfig;
        } catch (IOException ioe) {
        	LOG.error("Unable to prepare magic config from file: " + magicConfigFileName, ioe);
        	throw new RuntimeException("Unable to prepare magic config from file: " + magicConfigFileName, ioe);
        }
	}
	
	private static String resolvePlaceholders(String strVal, Properties properties) {
		resolver.setResolvedProperties(properties);
		return resolver.resolveStringValue(strVal);
	}
	
	private static Properties loadPlaceholders(String magicConfigSetupFile) {
		Properties properties = new Properties();

		LOG.info("Looking for magic config setup file: " + magicConfigSetupFile);
		
		// Load the properties from the magic config setup file
		try {
			InputStream is = new FileInputStream(new File(magicConfigSetupFile));
			properties.load(is);
			is.close();
		} catch (FileNotFoundException fnfe) {
			LOG.warn("Unable to find setup file " + magicConfigSetupFile);
		} catch (IOException ioe) {
			LOG.warn("Unable to load properties from setup file: " + magicConfigSetupFile, ioe);
		}
		
		return properties;
	}
}
